using System;
using System.Configuration;
using System.Web.Mvc;
using System.Web.Routing;
using EPiServer;
using EPiServer.Common;
using EPiServer.Common.Categories;
using EPiServer.Common.Comments;
using EPiServer.Common.Tags;
using EPiServer.Community.Blog;
using EPiServer.Core;
using EPiServer.Find;
using EPiServer.Find.ClientConventions;
using EPiServer.Web;
using JoelAbrahamsson.BlogExtensions;
using JoelAbrahamsson.Web.Common;
using JoelAbrahamsson.Web.ExtensionMethods;
using JoelAbrahamsson.Web.ModelBinders;
using JoelAbrahamsson.PageTypes;
using log4net;
using Mvc.ResourceCombine;
using StructureMap;
using StructureMap.Attributes;
using StructureMap.Pipeline;

namespace JoelAbrahamsson.Web
{
    public class Global : EPiServer.Global
    {
        protected void Application_Start(Object sender, EventArgs e)
        {
            ConfigureObjectFactory();

            RegisterRoutes(RouteTable.Routes);

            AddCustomModelBinders();

            ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());

            InitializationModule.FirstBeginRequest += InitializationModule_FirstBeginRequest;
        }

        void InitializationModule_FirstBeginRequest(object sender, EventArgs e)
        {
            UrlRewriteProvider.ConvertingToExternal += UrlRewriteProvider_ConvertingToExternal;
        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            if (RequestIsForDefaultDomain())
                return;

            if (RequestIsForStaticResource())
                return;

            string redirectUrl = Request.Url.ToString().Replace(Request.Url.Host, ConfigurationManager.AppSettings["defaultDomain"]);
            Response.Status = "301 Moved Permanently";
            Response.AddHeader("Location", redirectUrl);
            Response.End();
        }

        private bool RequestIsForDefaultDomain()
        {
            return Request.Url.Host.Equals(ConfigurationManager.AppSettings["defaultDomain"], StringComparison.InvariantCultureIgnoreCase);
        }

        private bool RequestIsForStaticResource()
        {
            string[] staticResourceExtensions = new [] { "png", "gif", "jpg", "swf" };

            foreach (string extension in staticResourceExtensions)
            {
                if(Request.Url.ToString().EndsWith(extension))
                    return true;
            }

            return false;
        }

        private void ConfigureObjectFactory()
        {
            ObjectFactory.Configure(x =>
                {
                    x.Scan(scanner =>
                               {
                                    scanner.TheCallingAssembly();
                                    scanner.Assembly("JoelAbrahamsson.BlogExtensions");
                                    scanner.AssembliesFromApplicationBaseDirectory(assembly => assembly.FullName.StartsWith("EPiAbstractions"));
                                    scanner.WithDefaultConventions();
                               }
                        );

                    x.ForRequestedType<ExtendedBlogHandlerFacade>().CacheBy(InstanceScope.Singleton).TheDefaultIsConcreteType<ExtendedBlogHandlerFacade>();

                    x.ForRequestedType<AvatarProvider>()
                        .Use<AvatarProvider>().WithCtorArg("defaultAvatarImagesPath")
                        .EqualToAppSetting("defaultAvatarImagesPath");

                    x.ForRequestedType<ICommentValidator>().TheDefault.Is.OfConcreteType<ProjectHoneyPotCommentValidator>()
                        .WithCtorArg("projectHoneyPotAccessKey").EqualToAppSetting("projectHoneyPotAccessKey")
                        .WithCtorArg("projectHoneyPostDnsSuffix").EqualToAppSetting("projectHoneyPostDnsSuffix");

                    x.ForRequestedType<ICommentValidator>().Use<SameIpEntryValidator>();

                    x.ForRequestedType<ICommentValidator>().Use<AkismetCommentValidator>()
                        .WithCtorArg("apiKey").EqualToAppSetting("akismetApiKey")
                        .WithCtorArg("blog").EqualToAppSetting("akismetBlogUrl");

                    x.ForRequestedType<ICommentAddedNotifier>().TheDefault.Is.OfConcreteType<EmailCommentAddedNotifier>();

                    x.ForRequestedType<ILog>().CacheBy(InstanceScope.PerRequest).Use((IContext context) => LogManager.GetLogger("ErrorLog"));

                    x.ForRequestedType<IClient>().CacheBy(InstanceScope.Singleton).Use(c => CreateSearchClient());
                });
        }

        private static IClient CreateSearchClient()
        {
            var client = Client.CreateFromConfig();

            client.Conventions.ForInstancesOf<Tag>()
                .ModifyContract(x => x.Properties.Clear());

            client.Conventions.ForInstancesOf<Tag>()
                .IncludeField(x => x.Name)
                .IncludeField(x => x.ID);

            client.Conventions.ForInstancesOf<EntityTag>()
                .ModifyContract(x => x.Properties.Clear());

            client.Conventions.ForInstancesOf<EntityTag>()
                .IncludeField(x => x.Tag);

            client.Conventions.ForInstancesOf<Category>()
                .ModifyContract(x => x.Properties.Clear());

            client.Conventions.ForInstancesOf<Category>()
                .IncludeField(x => x.Name)
                .IncludeField(x => x.ID);

            //client.Conventions.ForInstancesOf<Entry>()
            //    .ModifyContract(x => x.Properties.Clear());

            client.Conventions.ForInstancesOf<Entry>()
                .IdIs(x => x.ID)
                .ExcludeField(x => x.Author)
                .ExcludeField(x => x.AvgRating)
                .ExcludeField(x => x.Blog)
                .ExcludeField(x => x.CacheKey)
                .ExcludeField(x => x.ImageGallery)
                .ExcludeField(x => x.IsRatable)
                .ExcludeField(x => x.IsReported)
                .ExcludeField(x => x.NumRatings)
                .ExcludeField(x => x.NumUnreadComments)
                .ExcludeField(x => x.Published)
                .ExcludeField(x => x.ReadOnly)
                .ExcludeField(x => x.ReportData)
                .ExcludeField(x => x.SenderIP)
                .ExcludeField(x => x.Site)
                .IncludeField(x => x.GetSlug())
                .IncludeField(x => x.GetTeaserText())
                .FieldsOfType<string>().StripHtml();
            return client;
        }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.AddResourceCombineRoute("Resource Combine");
            routes.MapRouteLowercase(
                "OldRss",
                "syndication.axd",
                new { controller = "Error", action = "NotFound", aspxerrorpath = "/syndication.axd" }
                );
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("authui/{*pathInfo}");
            routes.IgnoreRoute("util/{*pathInfo}");
            routes.IgnoreRoute("pagefiles/{*pathInfo}");
            routes.IgnoreRoute("images/{*pathInfo}");
            routes.IgnoreRoute("blogfiles/{*pathInfo}");
            routes.MapRouteLowercase(
                "Home",
                "",
                new { controller = "Home", action = "Index", entryCount = 10 },
                new { entryCount = @"\d+" }
                );
            routes.MapRouteLowercase(
                null,
                "{entryCount}entries",
                new { controller = "Home", action = "Index" },
                new { entryCount = @"\d+" }
                );
            routes.MapRouteLowercase(
                "Search",
                "search",
                new { controller = "SiteSearch", action = "Index" }
                );
            routes.MapRouteLowercase(
                "WindowsLiveWriterLayout",
                "entry/wlwlayout",
                new { controller = "Entry", action = "WlwLayout" }
                );
            routes.MapRouteLowercase(
                "EPiServerWorldRss",
                "rss/episerverworld",
                new { controller = "RSS", action = "EpiserverWorld" }
                );
            routes.MapRouteLowercase(
                "Entry",
                "entry/{slug}/{action}",
                new { controller = "Entry", action = "Index" }
                );
            routes.MapRouteLowercase(
                "ArchiveMonth",
                "archive/month/{year}/{month}",
                new { controller = "Archive", action = "Month" },
                new { year = @"\d+", month = @"\d+" }
                );
            routes.MapRouteLowercase(
                "ArchiveCategory",
                "archive/category/{mainCategoryName}/{subCategoryName}",
                new { controller = "Archive", action = "Category" }
                );
            routes.MapRouteLowercase(
                "ArchiveTag",
                "archive/tag/{tagName}",
                new { controller = "Archive", action = "Tag" }
                );
            routes.MapRouteLowercase(
                "Error",
                "Error/{action}",
                new { controller = "Error", action = "Unknown" }
                );
            routes.MapRouteLowercase(
                "Sitemap",
                "Sitemap/{action}",
                new { controller = "Sitemap" }
                );
            routes.MapRouteLowercase(
                "Default",
                "{controller}/{slug}/{action}",
                new { controller = "Home", action = "Index", slug = "" }
                );
        }

        private void AddCustomModelBinders()
        {
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(BasePageData), new PageDataModelBinder());
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(Article), new PageDataModelBinder());
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(Blog), ObjectFactory.GetInstance<BlogModelBinder>());
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(Entry), ObjectFactory.GetInstance<EntryModelBinder>());
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(Comment), ObjectFactory.GetInstance<CommentModelBinder>());
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(IAuthor), ObjectFactory.GetInstance<AuthorModelBinder>());
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(ICategory), ObjectFactory.GetInstance<CategoryModelBinder>());
            System.Web.Mvc.ModelBinders.Binders.Add(typeof(ITag), ObjectFactory.GetInstance<TagModelBinder>());
        }

        void UrlRewriteProvider_ConvertingToExternal(object sender, UrlRewriteEventArgs e)
        {
            string queryID = e.Url.QueryCollection["id"];
            if (queryID == null)
                return;

            int pageID;
            if (queryID.Contains("_"))
            {
                RewriteToWorkPageUrl(e, queryID);
            }
            else if (int.TryParse(queryID, out pageID))
            {
                RewriteToPageUrl(e, pageID);
            }
        }

        private void RewriteToWorkPageUrl(UrlRewriteEventArgs e, string queryID)
        {
            int pageID;
            string[] splitQueryID = queryID.Split(
                new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);

            if (int.TryParse(splitQueryID[0], out pageID))
            {
                PageData pageData = DataFactory.Instance.GetPage(new PageReference(pageID));

                if (!(pageData is BasePageData))
                    return;

                BasePageData page = (BasePageData)pageData;
                string path = "/" + page.ControllerName + "/" + queryID + "/";
                RewiteUrl(e, path);
            }
        }

        private void RewiteUrl(UrlRewriteEventArgs e, string path)
        {
            e.Url.Path = path;
            e.Url.QueryCollection.Remove("id");
            e.Url.QueryCollection.Remove("epslanguage");
            e.IsMappableUrl = true;
            e.IsModified = true;
        }

        private void RewriteToPageUrl(UrlRewriteEventArgs e, int pageID)
        {
            PageData pageData = DataFactory.Instance.GetPage(new PageReference(pageID));
            
            if(!(pageData is BasePageData))
                return;

            BasePageData page = (BasePageData)pageData;

            string path = string.Format("/{0}", page.ControllerName);
            if(!string.IsNullOrEmpty(page.Slug))
                path += "/" + page.Slug + "/";

            RewiteUrl(e, path);
        }

        protected override void OnValidateRequestAccess(ValidateRequestAccessEventArgs e)
        {
            base.OnValidateRequestAccess(e);
            if (e.Cancel)
            {
                return;
            }
        }

        public void Application_EndRequest(object sender, EventArgs e)
        {
            HttpContextLifecycle.DisposeAndClearAll();
        }

        protected void Application_Error(object sender, EventArgs e)
        {
            string errorMessage = "Application error";
            if (Server.GetLastError() != null)
            {
                Exception lastError = Server.GetLastError();
                errorMessage = lastError.Message;
                errorMessage += "\n" + lastError.StackTrace;
                if (lastError.InnerException != null)
                {
                    errorMessage = lastError.InnerException.Message;
                    errorMessage += "\n" + lastError.InnerException.StackTrace;
                }
            }
            log4net.LogManager.GetLogger("ErrorLog").Error(errorMessage);
        }
    }
}
